#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>

template<class T>
class BlockQueue
{
    static const int defaultnum = 15;
public:
    BlockQueue(int maxcap = defaultnum):_maxcap(maxcap)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_c_cond, nullptr);
        pthread_cond_init(&_p_cond, nullptr);

        // _low_water = _maxcap / 3;
        // _high_water = _maxcap *2 / 3;
    }

    void push(const T& in)
    {
        pthread_mutex_lock(&_mutex);
        // if(_queue.size() == _maxcap)
        while(_queue.size() == _maxcap)
        {
            // 不能生产，去等待
            pthread_cond_wait(&_p_cond, &_mutex); // 1、等待的时候自动释放锁  2、因为唤醒而返回的时候，重新持有锁。
        }
        // 1、队列没满  2、被唤醒
        _queue.push(in);    // 阻塞队列，你想生产就直接能生产吗？不一定。要先确保生产条件满足，判断
        // if(_queue.size() > _high_water) 
        pthread_cond_signal(&_c_cond);
        pthread_mutex_unlock(&_mutex);
    }

    // 谁来唤醒呢？只有生产者最清楚自己数据满没满
    T pop()
    {
        pthread_mutex_lock(&_mutex);
        while(_queue.size() == 0)
        // if(_queue.size() == 0)
        {
            // 如果线程wait时候，被误唤醒了怎么办？因此这里要做到防止线程被伪唤醒
            pthread_cond_wait(&_c_cond, &_mutex);
        }
        // 
        T out = _queue.front();   // 你想消费，就能消费码？不一定，你得先确保消费条件满足
        _queue.pop();
        // if(_queue.size() < _low_water) 
        pthread_cond_signal(&_p_cond);
        pthread_mutex_unlock(&_mutex);

        return out;
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_c_cond);
        pthread_cond_destroy(&_p_cond);
    }
private:
    std::queue<T> _queue;   // 共享资源，queue被当做是整体来使用的。queue只有一份。但是共享资源也可以被看做多份。
    int _maxcap;     // 极值，最大到多少时就不能放入。
    // int _mincap;     // 极小值
    pthread_mutex_t _mutex;
    pthread_cond_t _c_cond;
    pthread_cond_t _p_cond;

    int _low_water;
    int _high_water;
};